#include <iostream>
#include <string>
#include <utility>
#include <vector>
#include <fstream>
#include <algorithm>
#include <filesystem>
#include <unordered_map>
#include <Poco/Net/POP3ClientSession.h>
#include <Poco/Net/MailMessage.h>
#include <Poco/Net/NetException.h>
#include <Poco/NumberFormatter.h>
#include <Poco/Net/MessageHeader.h>
#include <Poco/Net/PartHandler.h>
#include <Poco/StreamCopier.h>

std::string getErrorMsg(const int err_num) {
    char buf[512];
    if (_strerror_s(buf, sizeof(buf), nullptr) == 0) {
        return {buf};
    }

    return "unknown error " + std::to_string(err_num);
}


std::string sanitizeFilename(const std::string &filename) {
    std::string sanitized = filename;
    // 替换路径分隔符（/ 和 \）
    std::replace(sanitized.begin(), sanitized.end(), '/', '_');
    std::replace(sanitized.begin(), sanitized.end(), '\\', '_');

    // 可选：替换其他系统不允许的字符（如 :*?"<>| 等）
    const std::string illegal_chars = ":*?\"<>|";
    for (char c: illegal_chars) {
        std::replace(sanitized.begin(), sanitized.end(), c, '_');
    }

    return sanitized;
}

// 附件处理器类
class AttachmentHandler final : public Poco::Net::PartHandler {
public:
    explicit AttachmentHandler(std::filesystem::path save_path) : save_path_(std::move(save_path)) {
    }

    void handlePart(const Poco::Net::MessageHeader &header, std::istream &stream) override {
        // 获取文件名
        const std::string filename = extractFilename(header);
        if (filename.empty()) {
            return;
        }

        const std::string sanitize_filename = sanitizeFilename(filename);
        const std::filesystem::path full_path = save_path_ / "tmp" / sanitize_filename;

        try {
            std::filesystem::create_directories(full_path.parent_path());
        } catch (const std::filesystem::filesystem_error &e) {
            std::cerr << "create dir error:" << e.what() << std::endl;
            return;
        }


        std::ofstream ofs(full_path.wstring().c_str(), std::ios::binary);
        if (!ofs) {
            std::cerr << "open file " << full_path << " error " << getErrorMsg(errno) << std::endl;
            return;
        }

        // 复制流内容到文件
        try {
            Poco::StreamCopier::copyStream(stream, ofs);
            ofs.flush();
            std::cout << "write file " << full_path << " success" << std::endl;
            file_map_.emplace(sanitize_filename, full_path);
        } catch (const std::exception &e) {
            std::cerr << "write file error " << e.what() << std::endl;
        }
    }

    std::unordered_map<std::string, std::filesystem::path> &getFileMap() {
        return file_map_;
    }

private:
    static std::string extractFilename(const Poco::Net::MessageHeader &header) {
        // 定义一个局部函数处理具体提取逻辑，避免重复代码
        auto extract_from = [](const std::string &str, const std::string &prefix) {
            if (str.empty()) return std::string();

            const size_t start = str.find(prefix);
            if (start == std::string::npos) return std::string();

            // 提取前缀后的内容
            std::string value = str.substr(start + prefix.size());

            // 移除首尾引号（如果存在）
            if (!value.empty() && value.front() == '"') value.erase(0, 1);
            if (!value.empty() && value.back() == '"') value.pop_back();

            return value;
        };

        // 优先从Content-Disposition提取
        std::string name = extract_from(header.get("Content-Disposition", ""), "filename=");

        // 如果未提取到，再从Content-Type提取
        if (name.empty()) {
            name = extract_from(header.get("Content-Type", ""), "name=");
        }

        return name;
    }

    std::unordered_map<std::string, std::filesystem::path> file_map_;
    std::filesystem::path save_path_;
};

class EmailClient {
public:
    EmailClient(std::string server, int port,
                std::string user, std::string pass,
                std::string target, std::filesystem::path save_path)
        : server(std::move(server)), port(port),
          username(std::move(user)), password(std::move(pass)),
          expected_subject_(std::move(target)), save_path_(std::move(save_path)) {
    }

    void run() const {
        try {
            std::cout << "connecting to mail server " << server << ":" << port << std::endl;

            // 建立安全连接
            Poco::Net::POP3ClientSession session(server, port);

            std::cout << "connected, try login..." << std::endl;
            session.login(username, password);
            std::cout << "login success" << std::endl;

            // 获取邮件列表
            Poco::Net::POP3ClientSession::MessageInfoVec messages;
            session.listMessages(messages);
            std::cout << "found " << messages.size() << " mails" << std::endl;

            //从最近的邮件开始读
            std::reverse(messages.begin(), messages.end());

            // 处理每封邮件
            for (const auto &[id, size]: messages) {
                try {
                    std::cout << "process mail #" << id << " " << size << " bytes" << std::endl;

                    // 创建附件处理器
                    AttachmentHandler attachmentHandler(save_path_);
                    Poco::Net::MailMessage msg;

                    // 获取邮件内容
                    session.retrieveMessage(id, msg, attachmentHandler);

                    // 检查邮件标题
                    std::string subject = msg.getSubject();
                    if (!expected_subject_.empty() &&
                        !containsIgnoreCase(subject, expected_subject_)) {
                        continue;
                    }

                    std::cout << "match title: " << subject << std::endl;

                    const std::unordered_map<std::string, std::filesystem::path> &file_map = attachmentHandler.
                            getFileMap();
                    for (const auto &[fst, snd]: file_map) {
                        std::filesystem::path new_path = save_path_ / fst;
                        std::filesystem::rename(snd, new_path);
                        std::cout << "move file " << snd << " to " << new_path << std::endl;
                    }
                } catch (const std::exception &e) {
                    std::cerr << "process mail #" << id << " error " << e.what() << std::endl;
                }
            }

            session.close();
        } catch (const Poco::Net::POP3Exception &e) {
            std::cerr << "pop3 error " << e.what() << std::endl;
        }
        catch (const Poco::IOException &e) {
            std::cerr << "i/o error " << e.what() << std::endl;
        }
        catch (const std::exception &e) {
            std::cerr << "error " << e.what() << std::endl;
        }
    }

private:
    static bool containsIgnoreCase(const std::string &str, const std::string &substr) {
        const auto it = search(str.begin(), str.end(),
                               substr.begin(), substr.end(),
                               [](const char ch1, const char ch2) {
                                   return toupper(ch1) == toupper(ch2);
                               }
        );
        return it != str.end();
    }

    std::string server;
    int port;
    std::string username;
    std::string password;
    std::string expected_subject_;
    std::filesystem::path save_path_;
};

int main() {
    // 解析参数
    std::string server = "pop.126.com";
    int port = 110;
    std::string username = "xxxxx@126.com";
    std::string password = "xxxxxxxxxxxx";
    std::filesystem::path save_path = "D:\\mail\\";
    std::string expected_subject = "BOSS直聘";

    // 运行邮件客户端
    EmailClient client(server, port, username, password, expected_subject, save_path);
    client.run();

    return 0;
}
